﻿using System;
using System.Diagnostics;
using System.Globalization;
using System.Numerics;

namespace Bitcoin
{
    public class Temp_BitcoinUtility
    {
        /// <summary>
        ///     How many "Satoshis" there are in 0.01 BitCoins.
        /// </summary>
        /// <remarks>
        ///     A Satoshi is the smallest unit that can be transferred using BitCoin.
        /// </remarks>
        public const ulong Cent = 1000000;

        /// <summary>
        ///     How many "Satoshis" there are in a BitCoin.
        /// </summary>
        /// <remarks>
        ///     A Satoshi is the smallest unit that can be transferred using BitCoin.
        /// </remarks>
        public const ulong Coin = 100000000;

        public const long MAX_MONEY = 21000000*(long) Coin;

        /// <summary>
        ///     Returns the given value in Satoshis as a 0.12 type string.
        /// </summary>
        public static string BitcoinValueToFriendlyString(long value)
        {
            var negative = value < 0;
            if (negative)
                value = -value;
            var coins = value/(long) Coin;
            var cents = value%(long) Coin;
            return String.Format("{0}{1}.{2:00}", negative ? "-" : "", coins, cents/(long) Cent);
        }

        /// <summary>
        ///     Returns the given value in Satoshis as a 0.12 type string.
        /// </summary>
        public static string BitcoinValueToFriendlyString(ulong value)
        {
            var coins = value/Coin;
            var cents = value%Coin;
            return String.Format("{0}.{1:00}", coins, cents/Cent);
        }

        public static BigInteger DecodeCompactBits(long compact)
        {
            // high byte is length
            // next three bytes are the first three bytes of the large number
            // (the rest are zeros)
            // Note: As BigInteger is LE, need to fill in from the end
            // Also make the byte array one large so the first byte is zero to force positive number

            var size = (byte) (compact >> 24);
            var bytes = new byte[size + 1];
            if (size >= 1)
            {
                bytes[size - 1] = (byte) ((compact >> 16));
            }
            if (size >= 2) bytes[size - 2] = (byte) (compact >> 8);
            if (size >= 3) bytes[size - 3] = (byte) (compact >> 0);
            var result = new BigInteger(bytes);
            return result;
        }

        /// <summary>
        ///     Convert an amount expressed in the way humans are used to into Satoshis.
        /// </summary>
        public static ulong ToSatoshis(uint coins, uint cents)
        {
            Debug.Assert(cents < 100);
            var bi = coins*Coin;
            bi += cents*Cent;
            return bi;
        }

        /// <summary>
        ///     Convert an amount expressed in the way humans are used to into Satoshis.
        /// </summary>
        /// <remarks>
        ///     This takes string in a format understood by <see cref="double.Parse(string)" />,
        ///     for example "0", "1", "0.10", "1.23E3", "1234.5E-5".
        /// </remarks>
        /// <exception cref="System.ArithmeticException">If you try to specify fractional Satoshis.</exception>
        public static ulong ToSatoshis(string coins)
        {
            var value = Decimal.Parse(coins, NumberStyles.Float)*Coin;
            if (value != Math.Round(value))
            {
                throw new ArithmeticException();
            }
            return checked((ulong) value);
        }

        // The representation of nBits uses another home-brew encoding, as a way to represent a large
        // hash value in only 32 bits.


        //    public static BigInteger DecodeCompactBits2(long compact)
        //    {
        //        var size = (byte) (compact >> 24);
        //        var bytes = new byte[4 + size];
        //        bytes[3] = size;
        //        if (size >= 1)
        //        {
        //            bytes[4] = (byte) ((compact >> 16) );
        //        }
        //        if (size >= 2) bytes[5] = (byte) (compact >> 8);
        //        if (size >= 3) bytes[6] = (byte) (compact >> 0);
        //        return DecodeMpi(bytes);
        //    }

        //    /// <summary>
        //    /// MPI encoded numbers are produced by the OpenSSL BN_bn2mpi function. They consist of
        //    /// a 4 byte big endian length field, followed by the stated number of bytes representing
        //    /// the number in big endian format.
        //    /// </summary>
        //    private static BigInteger DecodeMpi(byte[] mpi)
        //    {
        //        // TODO: This whole bit seems redundant, as we just built the array above!

        //        var length = Temp_StreamUtility.ReadUint32Be(mpi, 0);
        //        var buf = new byte[length];
        //        Array.Copy(mpi, 4, buf, 0, (int) length);

        //        var temp = buf.Reverse().Concat(new Byte[] {0}).ToArray();
        //        var result = new BigInteger(temp);
        //        return result;
        //    }
    }
}